#ifndef NANE_SCRIPTFUNCTION_H
#   define NANE_SCRIPTFUNCTION_H

#   include "nane/ScriptSystem/ScriptFunctor.h"
#   include "nane/core/staticassert.h"
#   include "nane/core/IO.h"
#   include "nane/core/typetraits.h"
#   include "nane/core/function/ForwardParamT.h"

namespace nane
{
    class ScriptFunctionBase
        : public IScriptObject
    {
    public:
        virtual ~ScriptFunctionBase()
        {
        }
    };

    template<bool IsVoidRT, typename Functor, typename RT, typename P1 = NullType, typename P2 = NullType, typename P3 = NullType, typename P4 = NullType>
    struct ScriptFunctionCaller
    {
        static void Call( Functor _functor, IOutput* _results, P1 _p1, P2 _p2, P3 _p3, P4 _p4 )
        {
            RT result = _functor(_p1, _p2, _p3, _p4);
            IO<IOSafe>::Write<int>(_results, 1);
            ScriptSerializerType<RT>::Serialize(_results, result) != 0;
        }
    };

    template<bool IsVoidRT, typename Functor, typename RT, typename P1, typename P2, typename P3>
    struct ScriptFunctionCaller<IsVoidRT, Functor, RT, P1, P2, P3, NullType>
    {
        static void Call( Functor _functor, IOutput* _results, P1 _p1, P2 _p2, P3 _p3, NullType )
        {
            RT result = _functor(_p1, _p2, _p3);
            IO<IOSafe>::Write<int>(_results, 1);
            ScriptSerializerType<RT>::Serialize(_results, result) != 0;
        }
    };

    template<bool IsVoidRT, typename Functor, typename RT, typename P1, typename P2>
    struct ScriptFunctionCaller<IsVoidRT, Functor, RT, P1, P2, NullType, NullType>
    {
        static void Call( Functor _functor, IOutput* _results, P1 _p1, P2 _p2, NullType, NullType )
        {
            RT result = _functor(_p1, _p2);
            IO<IOSafe>::Write<int>(_results, 1);
            ScriptSerializerType<RT>::Serialize(_results, result) != 0;
        }
    };

    template<bool IsVoidRT, typename Functor, typename RT, typename P1>
    struct ScriptFunctionCaller<IsVoidRT, Functor, RT, P1, NullType, NullType, NullType>
    {
        static void Call( Functor _functor, IOutput* _results, P1 _p1, NullType, NullType, NullType )
        {
            RT result = _functor(_p1);
            IO<IOSafe>::Write<int>(_results, 1);
            ScriptSerializerType<RT>::Serialize(_results, result);
        }
    };

    template<bool IsVoidRT, typename Functor, typename RT>
    struct ScriptFunctionCaller<IsVoidRT, Functor, RT, NullType, NullType, NullType, NullType>
    {
        static void Call( Functor _functor, IOutput* _results, NullType, NullType, NullType, NullType )
        {
            RT result = _functor();
            IO<IOSafe>::Write<int>(_results, 1);
            ScriptSerializerType<RT>::Serialize(_results, result) != 0;
        }
    };

    template<typename Functor, typename RT, typename P1, typename P2, typename P3, typename P4>
    struct ScriptFunctionCaller<true, Functor, RT, P1, P2, P3, P4>
    {
        static void Call( Functor _functor, IOutput* _results, P1 _p1, P2 _p2, P3 _p3, P4 _p4 )
        {
            _functor(_p1, _p2, _p3, _p4);
            IO<IOSafe>::Write<int>(_results, 0);
        }
    };

    template<typename Functor, typename RT, typename P1, typename P2, typename P3>
    struct ScriptFunctionCaller<true, Functor, RT, P1, P2, P3, NullType>
    {
        static void Call( Functor _functor, IOutput* _results, P1 _p1, P2 _p2, P3 _p3, NullType )
        {
            _functor(_p1, _p2, _p3);
            IO<IOSafe>::Write<int>(_results, 0);
        }
    };

    template<typename Functor, typename RT, typename P1, typename P2>
    struct ScriptFunctionCaller<true, Functor, RT, P1, P2, NullType, NullType>
    {
        static void Call( Functor _functor, IOutput* _results, P1 _p1, P2 _p2, NullType, NullType )
        {
            _functor(_p1, _p2);
            IO<IOSafe>::Write<int>(_results, 0);
        }
    };

    template<typename Functor, typename RT, typename P1>
    struct ScriptFunctionCaller<true, Functor, RT, P1, NullType, NullType, NullType>
    {
        static void Call( Functor _functor, IOutput* _results, P1 _p1, NullType, NullType, NullType )
        {
            _functor(_p1);
            IO<IOSafe>::Write<int>(_results, 0);
        }
    };

    template<typename Functor, typename RT>
    struct ScriptFunctionCaller<true, Functor, RT, NullType, NullType, NullType, NullType>
    {
        static void Call( Functor _functor, IOutput* _results, NullType, NullType, NullType, NullType )
        {
            _functor();
            IO<IOSafe>::Write<int>(_results, 0);
        }
    };

    template<class Functor, typename RT, typename P1, typename P2, typename P3, typename P4>
    struct ScriptFunctionSpecialization
    {
        static bool DoCall( Functor _functor, IInput* _args, IOutput* _results )
        {
            typedef typename ForwardParamT<P1>::Type T1;
            typedef typename ForwardParamT<P2>::Type T2;
            typedef typename ForwardParamT<P3>::Type T3;
            typedef typename ForwardParamT<P4>::Type T4;
            
            T1 arg1; 
            T2 arg2; 
            T3 arg3; 
            T4 arg4;
            
            int argsNum = 0;
            IO<IOSafe>::Read<int>(_args, &argsNum);
            if( argsNum != NonVoidOrNullCount<T1, T2, T3, T4>::value )
            {
                // invalid args num
                LOG_ERROR("Invalid arguments number");
                return false;
            }
            //if( ScriptSerializerType<P1, P2, P3, P4>::deserialize(_args, &arg1, &arg2, &arg3, &arg4) == true )
            if( ScriptSerializerType<T1>::Deserialize(_args, &arg1) == true
               && ScriptSerializerType<T2>::Deserialize(_args, &arg2) == true
               && ScriptSerializerType<T3>::Deserialize(_args, &arg3) == true
               && ScriptSerializerType<T4>::Deserialize(_args, &arg4) == true )
            {
                ScriptFunctionCaller<IsVoidT<RT>::value, Functor, RT, T1, T2, T3, T4>::Call(_functor, _results, arg1, arg2, arg3, arg4);
                return true;
            }
            else
            {
                // failed to unpack arguments
                LOG_ERROR("failed to unpack arguments");
            }
            return false;
        }
    };

    template<typename Functor>
    class ScriptFunctionAdapter
        : public ScriptFunctionBase
    {
    public:
        ScriptFunctionAdapter( Functor _functor )
            : m_functor( _functor )
        {
        }

    public:
        bool Invoke( IInput* _args, IOutput* _results )
        {
            return ScriptFunctionSpecialization<Functor
                                                 , typename Functor::TReturn
                                                 , typename Functor::TParam1
                                                 , typename Functor::TParam2
                                                 , typename Functor::TParam3
                                                 , typename Functor::TParam4>::DoCall(m_functor, _args, _results);
        }
        
        const char* ToString()
        {
            return "ScriptFunctionAdapter";
        }

    private:
        Functor m_functor;
    };

} // namespace nane

#endif	// NANE_SCRIPTFUNCTION_H
